﻿using Alternet.Editor;
using Alternet.Editor.TextSource;
using Alternet.Scripter;
using Alternet.Syntax;
using Alternet.Syntax.Parsers.Roslyn;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Markup;

namespace OrBitDataModule.Controls
{
    public partial class CodeEditor : UserControl
    {
        private readonly CsParser csParser = new CsParser();
        private readonly TextSource csharpSource = new TextSource();

        private readonly int errImageIndex = 43;
        private readonly int warningImageIndex = 45;

        /// <summary>
        /// 脚本保存后事件
        /// </summary>
        public event EventHandler<string> OnSourceCodeSaved;

        /// <summary>
        /// 编辑器是否只读
        /// </summary>
        public bool ReadOnly
        {
            get => syntaxEdit.ReadOnly; set { syntaxEdit.ReadOnly = value; UpdateButtons(); }
        }
        /// <summary>
        /// 编辑器脚本代码
        /// </summary>
        public string Code { get => syntaxEdit.Source.Text; }
        public CodeEditor()
        {
            InitializeComponent();
            syntaxEdit.Lexer = csParser;
            csharpSource.Lexer = csParser;
            csharpSource.HighlightReferences = true;
            syntaxEdit.Source = csharpSource;
            syntaxEdit.CodeCompletionBox.Sorted = true;
            syntaxEdit.Gutter.Options = syntaxEdit.Gutter.Options | GutterOptions.PaintLineNumbers; //| GutterOptions.PaintLinesOnGutter;

            syntaxEdit.CodeCompletionBox.Images = imageList1;
            syntaxEdit.SourceChanged += SyntaxEdit_SourceChanged;
            syntaxEdit.Outlining.AllowOutlining = true;
            InitScriptRun();
            //隐藏
            btn_save.Visible = false;

        }

        /// <summary>
        /// 初始化脚本运行
        /// </summary>
        private void InitScriptRun()
        {
            scriptRun.ScriptSource.WithDefaultReferences();
            scriptRun.ScriptHost.GenerateModulesOnDisk = false;
            scriptRun.AssemblyKind = ScriptAssemblyKind.DynamicLibrary;
            scriptRun.ScriptLanguage = ScriptLanguage.CSharp;
            //默认添加数据模块dll的引用
            AddReference(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "OrBitDataModule.dll"));
            AddLocalReference();

        }

        public (bool Result, string Message) CompileScript()
        {
            scriptRun.ScriptSource.FromScriptCode(syntaxEdit.Source.Text);
            scriptRun.Compile();
            return (scriptRun.ScriptHost.CompilerErrors.Count() == 0, string.Join("\r\n", scriptRun.ScriptHost.CompilerErrors.Select(x => x.ToString()).ToArray()));
        }

        public void AddReference(string dllFilePath)
        {
            scriptRun.ScriptSource.References.Add(dllFilePath);
            csParser.Repository.RegisterAssembly(dllFilePath);
        }

        private void AddLocalReference()
        {
            AddReference("HttpLib.dll");
            AddReference("System.Data.dll");

            string directory = AppDomain.CurrentDomain.BaseDirectory;
            foreach (string file in Directory.GetFiles(directory, "*.dll"))
            {
                try
                {
                    if (Path.GetFileName(file).StartsWith("System.") && Path.GetFileName(file) != "System.EnterpriseServices.Wrapper.dll")
                    {
                        AddReference(Path.GetFileName(file));
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"加载 {Path.GetFileName(file)} 时出错：{ex.Message}");
                }
            }
        }

        private void SyntaxEdit_SourceChanged(object sender, SourceChangedEventArgs e)
        {
            UpdateCodeWindows();
        }

        public void LoadSource(string scriptCode)
        {
            using (TextReader reader = new StringReader(scriptCode))
            {
                csharpSource.LoadStream(reader);
                UpdateCodeWindows();
            }
        }

        public void SaveSource()
        {
            string sourceCode = syntaxEdit.Source.Text;
            OnSourceCodeSaved?.Invoke(this, sourceCode);
            UpdateCodeWindows();
        }

        private void Btn_save_Click(object sender, EventArgs e)
        {
            SaveSource();
        }
        private void UpdateCodeWindows()
        {
            FillErrors(syntaxEdit.Lexer as ISyntaxParser);
            UpdateButtons();
        }

        private void FillErrors(ISyntaxParser parser)
        {
            lvErrors.Items.Clear();
            if (parser != null)
            {
                IList<ISyntaxError> list = new List<ISyntaxError>();
                parser.GetSyntaxErrors(list);
                if (syntaxEdit != null)
                {
                    for (int i = 0; i < list.Count; i++)
                    {
                        string[] error = { list[i].Position.Y.ToString(), list[i].Position.X.ToString(), list[i].Description };
                        ListViewItem item = new ListViewItem(error);
                        switch (list[i].ErrorType)
                        {
                            case SyntaxErrorType.Error:
                                item.ImageIndex = errImageIndex;
                                break;
                            case SyntaxErrorType.Warning:
                                item.ImageIndex = warningImageIndex;
                                break;
                        }

                        item.Tag = list[i];
                        lvErrors.Items.Add(item);
                    }
                }
            }
        }
        private void UpdateButtons()
        {
            bool enabled = syntaxEdit != null;
            bool canCut = enabled && syntaxEdit.Selection.CanCut();
            bool canCopy = enabled && syntaxEdit.Selection.CanCopy();
            bool canUndo = enabled && syntaxEdit.Source.CanUndo();
            bool canRedo = enabled && syntaxEdit.Source.CanRedo();
            bool canPaste = enabled && syntaxEdit.Selection.CanPaste();

            btn_save.Enabled = enabled;
            btn_find.Enabled = enabled;
            btn_replace.Enabled = enabled;
            btn_goto.Enabled = enabled;

            btn_cut.Enabled = canCut;
            btn_copy.Enabled = canCopy;
            btn_paste.Enabled = canPaste;

            btn_undo.Enabled = canUndo;
            btn_redo.Enabled = canRedo;

            btn_comment.Enabled = canUndo;
            btn_unComment.Enabled = canUndo;
        }

        private void Btn_cut_Click(object sender, EventArgs e)
        {
            if (syntaxEdit.Selection.CanCut())
                syntaxEdit.Selection.Cut();
        }

        private void Btn_copy_Click(object sender, EventArgs e)
        {
            if (syntaxEdit.Selection.CanCopy())
                syntaxEdit.Selection.Copy();
        }

        private void Btn_paste_Click(object sender, EventArgs e)
        {
            if (syntaxEdit.Selection.CanPaste())
                syntaxEdit.Selection.Paste();
        }

        private void Btn_undo_Click(object sender, EventArgs e)
        {
            if (syntaxEdit.Source.CanUndo())
            {
                syntaxEdit.Source.Undo();
            }
        }

        private void Btn_redo_Click(object sender, EventArgs e)
        {
            if (syntaxEdit.Source.CanRedo())
                syntaxEdit.Source.Redo();
        }

        private void Btn_find_Click(object sender, EventArgs e)
        {
            syntaxEdit.DisplaySearchDialog();
        }

        private void Btn_replace_Click(object sender, EventArgs e)
        {
            syntaxEdit.DisplayReplaceDialog();
        }

        private void Btn_goto_Click(object sender, EventArgs e)
        {
            syntaxEdit.DisplayGotoLineDialog();
        }

        private void Btn_comment_Click(object sender, EventArgs e)
        {
            syntaxEdit.Selection.CommentSelection();
        }

        private void Btn_unComment_Click(object sender, EventArgs e)
        {
            syntaxEdit.Selection.UncommentSelection();
        }

        private void Btn_upload_Click(object sender, EventArgs e)
        {
            SaveSource();
        }
    }
}
